use crate::{
  ast::Expression,
  lexer::{self, Lexer, Token},
};

pub struct Parser {
  lexer: Lexer,
}

impl Parser {
  pub fn new(lexer: Lexer) -> Self {
    Self { lexer }
  }

  pub fn parse(&mut self) -> Expression {
    self.parse_maths_expression(0.0)
  }

  fn parse_maths_expression(&mut self, precedence: f32) -> Expression {
    let mut lhs = match self.lexer.next() {
      Token::Atom(ch) => Expression::Atom(ch),
      Token::Op('(') => {
        // 遇到括号,解析括号中的表达式
        let lhs = self.parse_maths_expression(0.0);
        assert_eq!(self.lexer.next(), Token::Op(')'));
        lhs
      }
      Token::Op(op) => {
        // 如果是前缀运算符
        let ((), r_bp) = self.prefix_binding_power(op);
        let rhs = self.parse_maths_expression(r_bp);
        Expression::Operation(op, vec![rhs])
      }
      t => panic!("Invalid token: {t:?}"),
    };
    loop {
      let op = match self.lexer.peek() {
        Token::Op(')') => break,
        Token::Op(op) => op,
        Token::Eof => break,
        t => panic!("Invalid token: {t:?}"),
      };

      if let Some((l_bp, ())) = self.postfix_binding_power(op) {
        // 如果右边的优先级小于左边的优先级,就结束此次解析,返回左边的表达式
        if l_bp < precedence {
          break;
        }
        self.lexer.next();
        lhs = if op == '[' {
          let rhs = self.parse_maths_expression(0.0);
          assert_eq!(self.lexer.next(), Token::Op(']'));
          Expression::Operation(op, vec![lhs, rhs])
        } else {
          Expression::Operation(op, vec![lhs])
        };
        continue;
      }

      if let Some((l_bp, r_bp)) = self.infix_binding_power(op) {
        // 如果右边的优先级小于左边的优先级,就结束此次解析,返回左边的表达式
        if l_bp < precedence {
          break;
        }
        self.lexer.next();
        let rhs = self.parse_maths_expression(r_bp);
        lhs = Expression::Operation(op, vec![lhs, rhs]);
        continue;
      }

      break;
    }

    lhs
  }

  // 中缀表达式
  fn infix_binding_power(&self, op: char) -> Option<(f32, f32)> {
    let pre = match op {
      '=' => (0.2, 0.1),
      '+' | '-' => (1.0, 1.1),
      '*' | '/' => (2.0, 2.1),
      '^' | '√' => (3.1, 3.0),
      '.' => (6.0, 6.1), // (6.0, 6.1) 左结合,  (6.1, 6.0) 右结合
      _ => {
        eprintln!("Unknown infix operator: {op:?}");
        return None;
      }
    };
    Some(pre)
  }

  // 后缀表达式
  fn postfix_binding_power(&self, op: char) -> Option<(f32, ())> {
    let pre = match op {
      '[' => (5.0, ()),
      _ => {
        eprintln!("Unknown postfix operator: {op:?}");
        return None;
      }
    };
    Some(pre)
  }

  // 前缀表达式
  fn prefix_binding_power(&self, op: char) -> ((), f32) {
    match op {
      '!' | '-' => ((), 4.0),
      _ => panic!("Unknown prefix operator: {op:?}"),
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use crate::lexer::Lexer;

  #[test]
  fn test_1() {
    let lexer = Lexer::new("1");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "1");
  }

  #[test]
  fn test_2() {
    let lexer = Lexer::new("1 + 2");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(+ 1 2)");
  }

  #[test]
  fn test_3() {
    let lexer = Lexer::new("1 + 2 * 3");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(+ 1 (* 2 3))");
  }

  #[test]
  fn test_4() {
    let lexer = Lexer::new("a * 2 * b");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(* (* a 2) b)");
  }

  #[test]
  fn test_5() {
    let lexer = Lexer::new("a + b * 2 * c + a / 4");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(+ (+ a (* (* b 2) c)) (/ a 4))");
  }

  #[test]
  fn test_6() {
    let lexer = Lexer::new("2 + b * 5 - 3 / 5 + 5 -3");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(- (+ (- (+ 2 (* b 5)) (/ 3 5)) 5) 3)");
  }

  #[test]
  fn test_7() {
    let lexer = Lexer::new("(2 + b) * 5 ");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(* (+ 2 b) 5)");
  }

  #[test]
  fn test_8() {
    let lexer = Lexer::new("(((a)))");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "a");
  }

  #[test]
  fn test_9() {
    let lexer = Lexer::new("a + b * 2 * (c + a) / 4");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(+ a (/ (* (* b 2) (+ c a)) 4))");
  }

  #[test]
  fn test_10() {
    let lexer = Lexer::new("a + b * c ^ 4");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(+ a (* b (^ c 4)))");
  }

  #[test]
  fn test_11() {
    let lexer = Lexer::new("a + 2 √ 4 * b");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(+ a (* (√ 2 4) b))");
  }

  #[test]
  fn test_12() {
    let lexer = Lexer::new("a + 2 √ (4 * b)");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(+ a (√ 2 (* 4 b)))");
  }

  #[test]
  fn test_13() {
    let lexer = Lexer::new("a ^ b ^ 2");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(^ a (^ b 2))");
  }

  #[test]
  fn test_14() {
    let lexer = Lexer::new("a.b.c.d");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(. (. (. a b) c) d)");
  }

  #[test]
  fn test_15() {
    let lexer = Lexer::new("--1 * -2");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(* (- (- 1)) (- 2))");
  }

  #[test]
  fn test_16() {
    let lexer = Lexer::new("!9");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "(! 9)");
  }

  #[test]
  fn test_17() {
    let lexer = Lexer::new("x[0][1]");
    let mut parser = Parser::new(lexer);
    let expr = parser.parse();
    assert_eq!(expr.to_string(), "([ ([ x 0) 1)");
  }
}
